﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace voicedMelody.FeatureExtraction
{
    public class WaveUtil
    {
        private const double EnergyThreshold = 0.05;

        private const float SilenceAmplitudeThreshold = 0.02f;

        private const float MinimumSilenceLength = 0.05f; //sec

        //not fineshed
        //private static Signal RemoveSilence(Signal signal)
        //{
        //    int bytePerSample = signal.RawData.Length / signal.Samples;

        //    List<byte> dataBuffer = new List<byte>();
        //    List<byte> newData = new List<byte>();

        //    float[] data = new float[signal.Samples];
        //    signal.CopyTo(data);

        //    for (int i = 0; i <= signal.RawData.Length - bytePerSample; i += bytePerSample)
        //    {
        //        byte[] buffer = new byte[bytePerSample];
        //        Array.Copy(signal.RawData, i, buffer, 0, bytePerSample);

        //        int sample;

        //        if (bytePerSample == 4)
        //        {
        //            sample = BitConverter.ToInt32(signal.RawData, i);
        //        }
        //        else
        //        {
        //            throw new NotImplementedException();
        //        }

        //        if (sample > SilenceAmplitudeThreshold)
        //        {
        //            newData.AddRange(buffer);
        //        }
        //    }

        //    int signalLength = newData.Count / bytePerSample / signal.Channels;
        //    Signal newSignal = new Signal(newData.ToArray(), signal.Channels, signalLength, signal.SampleRate, signal.SampleFormat);

        //    return newSignal;
        //}

        //public static List<Signal> Split(string fileName, int samplesPerFrame, float ovelapping = 0, bool withThreshold = true)
        //{
        //    List<string> removedFrames = new List<string>();

        //    WaveDecoder decoder = new WaveDecoder(fileName);
        //    Signal signal = decoder.Decode();

        //    int bytePerSample = signal.RawData.Length / signal.Samples;
        //    int bytesPerFrame = samplesPerFrame * signal.Channels * bytePerSample;

        //    int frameCount = signal.RawData.Length / bytesPerFrame;

        //    double energy = signal.GetEnergy();
        //    double energyPerFrame = energy / frameCount;

        //    int step = (int)(bytesPerFrame * (1 - ovelapping));

        //    List<Signal> frames = new List<Signal>();
        //    for (int i = 0; i <= signal.RawData.Length - bytesPerFrame; i += step)
        //    {
        //        byte[] buffer = new byte[bytesPerFrame];
        //        Array.Copy(signal.RawData, i, buffer, 0, bytesPerFrame);

        //        int signalLength = buffer.Length / bytePerSample / signal.Channels;
        //        Signal frame = new Signal(buffer, signal.Channels, signalLength, signal.SampleRate, signal.SampleFormat);

        //        double frameEnergy = frame.GetEnergy();
        //        if (!withThreshold || frameEnergy >= (EnergyThreshold * energyPerFrame))
        //        {
        //            frames.Add(frame);
        //        }
        //        else
        //        {
        //            removedFrames.Add(fileName);
        //        }
        //    }

        //    return frames;
        //}

        //public static List<float[]> SplitOnRowData(string fileName, int samplesPerFrame, float overlaping = 0, bool withThreshold = true)
        //{
        //    List<Signal> signals = Split(fileName, samplesPerFrame, overlaping);
        //    List<float[]> data = new List<float[]>();
        //    foreach (Signal signal in signals)
        //    {
        //        float[] framed = new float[signal.Length];
        //        signal.CopyTo(framed);
        //        data.Add(framed);
        //    }
        //    return data;
        //}

        public static SignalData GetSignalData(string fileName)
        {
            WaveDecoder decoder = new WaveDecoder(fileName);
            Signal signal = decoder.Decode();

            if (signal.Channels > 1)
            {
                throw new NotSupportedException("Number of chanals must be 1");
            }

            float[] data = new float[signal.Length];
            signal.CopyTo(data);
            return new SignalData { Data = data, SampleRate = signal.SampleRate };
        }

        public static SignalData RemoveSilence(SignalData signal)
        {
            List<float> newData = new List<float>();
            List<float> buffer = new List<float>();

            int bufferSize = (int)(signal.SampleRate * MinimumSilenceLength);
            bool hasSignificantData = false;
            int removedCount = 0;

            foreach (float sample in signal.Data)
            {
                if (buffer.Count >= bufferSize)
                {
                    if (hasSignificantData)
                    {
                        newData.AddRange(buffer);
                    }
                    else
                    {
                        removedCount++;
                    }

                    buffer.Clear();
                    hasSignificantData = false;
                }

                buffer.Add(sample);

                if (sample > SilenceAmplitudeThreshold)
                {
                    hasSignificantData = true;
                }

            }

            return new SignalData { Data = newData.ToArray(), SampleRate = signal.SampleRate };
        }

        public static List<SignalData> Split(SignalData signalData, int samplesPerFrame, float ovelapping, bool withThreshold = true)
        {
            double energy = signalData.Data.Sum(o => o * o);

            double energyPerFrame = energy / (signalData.Length / samplesPerFrame);

            int step = (int)(samplesPerFrame * (1 - ovelapping));

            List<SignalData> frames = new List<SignalData>();
            int removed = 0;
            for (int i = 0; i <= signalData.Length - samplesPerFrame; i += step)
            {
                float[] frame = new float[samplesPerFrame];
                Array.Copy(signalData.Data, i, frame, 0, samplesPerFrame);

                double frameEnergy = frame.Sum(o => o * o);
                if (!withThreshold || frameEnergy >= (EnergyThreshold * energyPerFrame))
                {
                    SignalData framed = new SignalData { Data = frame, SampleRate = signalData.SampleRate };
                    frames.Add(framed);
                }
                else
                {
                    removed++;
                }
            }

            return frames;
        }

        public static SignalData Normalize(SignalData signalData)
        {
            float max = signalData.Data.Max();

            float[] data = new float[signalData.Length];

            for (int i = 0; i < signalData.Length; i++)
            {
                data[i] = signalData.Data[i] / max;
            }

            return new SignalData { Data = data, SampleRate = signalData.SampleRate };
        }

        public static List<float[]> GetPreparedData(string fileName, int samplesPerFrame, float overlapping,
            bool withThreshold = true)
        {
            SignalData signalData = GetSignalData(fileName);
            SignalData normalized = Normalize(signalData);
            SignalData withoutSilence = RemoveSilence(normalized);

            List<SignalData> frames = Split(withoutSilence, samplesPerFrame, overlapping, withThreshold);
            return frames.Select(o => o.Data).ToList();
        }
    }
}
